PHP  
downloads | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
search for in the  
<HTTP headers_sent>
view the version of this page
Last updated: Sat, 19 Apr 2003

header

(PHP 3, PHP 4 )

header -- Sendet einen HTTP-Header

Beschreibung

int header ( string string [, bool replace [, int http_reponse_code]])

header() wird zum Senden von HTTP Anfangsinformationen (Headern) benutzt. Weitere Informationen über HTTP Header finden Sie unter HTTP/1.1 specification.

Der optionale Parameter replace gibt an, ob der Header einen vorhergehenden Header ersetzten soll, oder ob ein zweiter Header des selben Typs hinzugefügt werden soll. Standardmäßig wird ersetzt. Sie können jedoch als das zweite Argument FALSE übergeben, und so mehrere Header desselben Typs erzwingen. Zum Beispiel:

<?php
header('WWW-Authenticate: Negotiate');
header('WWW-Authenticate: NTLM', FALSE);
?>

Der zweite optionale Parameter http_response_code spezifiziert den HTTP Response Code. (Dieser Parameter ist in PHP 4.3.0 und höher verfügbar.)

Es gibt zwei Spezialfälle von Header-Aufrufen. Der erste ist ein Header, der mit "HTTP/" beginnt (ob groß- oder Kleinschreibung ist nicht relevant) und zum Herausfinden des zu sendenden HTTP Statuscodes verwendet wird. Wenn Sie zum Beispiel Apache konfiguriert haben, um ein PHP Skript zum Bearbeiten von Anforderungen fehlender Dateien (mittels der ErrorDocument Direktive) zu verwenden, möchten Sie bestimmt sicherstellen, dass Ihr Skript den passenden Statuscode generiert.

<?php
  header("HTTP/1.0 404 Not Found");
?>

Anmerkung: Die HTTP Status Header Zeile wird immer die erste zum Client gesendete sein, egal ob der aktuelle header() - Aufruf der erste ist oder nicht. Der Status kann mittels header() jederzeit mit einer neuen Statuszeile überschrieben werden, es sei denn, dass die HTTP Header bereits gesendet wurden.

Anmerkung: In PHP 3 funktioniert dies nur, wenn PHP als Apache Modul kompiliert wurde. Sie können den gleichen Effekt erzielen, wenn Sie den Status Header verwenden.

<?php
header("Status: 404 Not Found");
?>

Der zweite Spezialfall ist der "Location:" Header. Es wird nicht nur der Header an den Browser geschickt, sondern auch ein REDIRECT (302) Statuscode, wenn nicht bereits ein 3xx Statuscode gesendet wurde.

<?php
header("Location: http://www.example.com/"); // Umleitung des Browsers
exit;                                        // Sicherstellen, dass nicht trotz Umleitung
                                             // der nachfolgende Code ausgeführt wird. 
?>

Anmerkung: HTTP/1.1 verlangt einen absoluten URI inkl. dem Schema, Hostnamen und absoluten Pfad als Argument von Location:, aber manche Clients akzeptieren auch relative URIs. Gewöhnlich können Sie mittels $_SERVER['HTTP_HOST'], $_SERVER['PHP_SELF'] und dirname() aus einem relativen Link einen absoluten URI selbst erstellen:

<?php
header("Location: http://".$_SERVER['HTTP_HOST']
                      .dirname($_SERVER['PHP_SELF'])
                      ."/".$relative_url);
?>

PHP-Skripte erzeugen oft dynamische Inhalte, die weder vom Browser noch von irgendeinem Proxy zwischen Web-Server und Client-Browser gepuffert ("gecached") werden sollen bzw. dürfen. Bei vielen Proxies und Browsern kann das Cachen wie folgt unterbunden werden:

<?php
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");    // Datum aus Vergangenheit
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
                                                     // immer geändert
header("Cache-Control: no-store, no-cache, must-revalidate");  // HTTP/1.1
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");                          // HTTP/1.0
?>

Anmerkung: Es könnte auch sein, dass Ihre Seiten nicht zwischengespeichert werden, auch wenn Sie obigen Header nicht ausgeben. Es gibt eine Anzahl Optionen, welche die Benutzer in deren Browser einstellen können, um das standardmäßige Verhalten bezüglich des Caching zu verändern. Durch das Senden obiger Header sollten Sie irgendwelche Einstellungen, welche die Ausgabe Ihres Skriptes zwischenspeichern würden, außer Kraft setzen.

Weiters können Sie session_cache_limiter() und die Konfigurationsoption session.cache_limiter verwenden, um die korrekten Header bezüglich Caching automatisch generieren zu lassen, sollten Sie Sessions benutzen.

Beachten Sie, dass header() aufgerufen werden muss, bevor eine Ausgabe gesendet wurde, egal ob normale HTML Tags, leere Zeilen in einer Datei oder von PHP. Ein weit verbreiteter Fehler ist mittels include(), require(), oder anderen Dateizugriffsmethoden Code zu lesen, und so unbewusst Leerzeichen oder Leerzeilen auszugeben, bevor header() aufgerufen wird. Das gleiche Problem besteht auch bei Verwendung einer einzigen PHP/HTML Datei.

<html>
<?php
// Dies funktioniert nicht. Beachten Sie die
// obige Ausgabe noch vor dem header()-Aufruf 
header('Location: http://www.example.com/');
?>

Anmerkung: In PHP 4 können Sie dieses Problem umgehen, indem Sie Output Buffering benutzen (mit dem Overhead, dass alle Ihre Ausgaben an den Browser gepuffert werden, bis Sie diese Senden). Um dies zu tun, verwenden Sie in Ihrem Skript ob_start() und ob_end_flush(), oder setzten die Konfigurationsdirektive output_buffering in der php.ini bzw. in den Server Konfigurationsdateien.

Wollen Sie den Benutzer auffordern, die von Ihnen gesendeten Daten wie z.B. eine generierte PDF Datei zu speichern, können Sie den Header Content-Disposition verwenden, um einen empfohlenen Dateinamen anzubieten und den Browser zu zwingen, den Dialog zum Speichern anzuzeigen.

<?php
// Wir werden eine PDF Datei ausgeben
header("Content-type: application/pdf");

// Es wird downloaded.pdf benannt
header("Content-Disposition: attachment; filename=downloaded.pdf");

// Die originale PDF Datei heißt original.pdf
readfile('original.pdf');
?>

Anmerkung: Der Microsoft Internet Explorer 4.01 hat einen Bug, der diese Funktionalität verhindert, und es gibt keinen Workaround. Auch in Microsoft Internet Explorer 5.5 existiert ein Bug, der dies behindert, dieser kann jedoch mittels einem Upgrade auf Service Pack 2 oder neuer behoben werden.

Anmerkung: Ist safe mode aktiviert, wird die UID des Skriptes dem realm Teil des Headers WWW-Authenticate (für HTTP Authentifizierung verwendet) hinzugefügt, sollten Sie diesen Header setzen.

Siehe auch headers_sent(), setcookie() und den Teil HTTP Authentifizierung.



User Contributed Notes
header
add a note add a note
lorenzNOSPAM at brown dot edu
04-Nov-2001 09:00

There is this nasty bug in IE 5 for Windows prior to service pack 2, described in Microsoft knowledgebase article Q281197 which causes a problem with redirecting.. if you submit a POST form to a page that uses header() to redirect to another page after processing the form data, then IE will not display some of the images on the page, if the user has an "external HTTP namespace handler" (RealDownload for example) installed. It took me a very long time to figure this out; I had no idea why my ads weren't displaying on IE for windows but worked fine everywhere else.
huib at NO_SPAMstack dot nl
04-Jan-2002 12:53

RFC 2616 section 13.13:
(...) History mechanisms and caches are different. In particular history
  mechanisms SHOULD NOT try to show a semantically transparent view of
  the current state of a resource. Rather, a history mechanism is meant
  to show exactly what the user saw at the time when the resource was
  retrieved. (...)

All browsers but Opera ignore this. This means Opera _always_ uses cached pages when using back, forward buttons. No matter what headers you send.

www.opera.com/support/supsearch/supsearch.cgi?options=index&name=594

mrNO_SPAM at soas dot ac dot uk
13-Jan-2002 05:58

IE5.5 SP2 is still not a complete cure for the header bug: using the standard method for deploying PDF files by buffering then sending the header() commands as recommended in PHP and PDFlib documentation doesn't work even with SP2 installed. It works fine with Navigator 4.75 on Win and Linux, and Linux+Konquerer 2.2.1 though.

The only general work-round I've found is to save the PDF to disc then send a HTML page with an auto-redirect using <meta http-equiv="refresh" content="0; URL=http://server/address.of.document.pdf"> in the head. Sharing violations could be a problem on a heavily used server though.

Malcolm Raggett.

mrNO_SPAM at soas dot ac dot uk
16-Jan-2002 11:52

Although the memory buffer technique doesn't work for IE5.5+SP2, if you apply the security patch Q313675.EXE (mid-December 2001) from the Microsoft site, the header("Content-type: application/pdf"); is interpreted correctly and now loads Acrobat.
rlourenco at uol dot com dot br
22-Feb-2002 12:26

If you want to force browsers to download the content and not to open it automaticaly, as a PDF or DOC files, use:

header("Content-type: application/octet-stream");

This will cause the browser to open the download dialog

niels at koekoek dot cjb dot net_ANTI-SP_AM
22-Feb-2002 08:44

If the HTTP-header doesn't work (eg. showing the error page), try the location-header like in example below.

header("Location: HTTP/1.0 400 Bad Request");

pbaylies at yahoo dot com
04-Apr-2002 06:57

I managed to get IE correctly downloading files from a PHP script, and the only extra header I have to send is Cache-Control.

This is all I send, where $mime is the appropriate MIME type for the file.

header("Cache-control: private");
header("Content-Type: " . $mime);

Hope this helps.

chamele0nDOT1ATemailDOTcom
17-Apr-2002 06:10

just made this for http1.1 compliant use of location: header redirections

function redirectTo($newUrl)
{
 // TODO: protocol possibilities need to be extended to indclude any other
 // desried protocols - ftp:// mnp:// whatever...  

 $newUrl = trim($newUrl);
 if (!(strpos($newUrl, "http://"/) === 0 || strpos($newUrl, "https://") === 0))
 {
     $newUrl = "http"
             . ($_SERVER['HTTPS'] == "on" ? "s" : "")
             . "://"
             . $_SERVER['HTTP_HOST']
             . (strpos($newUrl, "/") === 0? $newUrl : dirname($_SERVER['PHP_SELF']) . "/" . $newUrl);
 }

 header("Location: " . $newUrl);
exit();
}

rhett at mmind dot net
08-May-2002 11:18

if you want to have a link to an mp3 on your website, but don't want quicktime to load it, but rather download it to your machine, use this php script where $mp3name is the name of the mp3 without the .mp3 extension (do this to keep people from leeching mp3s from your site):

header("Cache-control: private");
header("Content-Type: application/mp3");
header("Content-Disposition: attachment; filename=$mp3name.mp3");

if you want to keep the .mp3 extension on your files on your site, simply take away the .mp3 in the last line of the script.

twocandles3000@hotmail
15-May-2002 10:33

The syntax "HTTP/1.0 403 Forbidden" neither works for PHP4 when used as CGI. It must be a module. When used as a CGI use the notation "Status: 403 Forbidden".
dpiper at stens dot com
24-May-2002 12:34

For inline images (JPEG for example):

header('Content-Type: image/jpeg');
header('Content-Disposition: inline; filename=file.jpg);

For attachments (Adobe PDF for example):

header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename=file.pdf);

NOTE: In Internet Explorer, the Content-Disposition header is important, otherwise it will be inline. 'Content-Disposition: attachment' will ALWAYS make IE download it.

NOTE: In Netscape, if you want to force it to be a download (i.e. not inline), use header('Content-Type: application/octet-stream').
Netscape doesn't appear to care about the Content-Disposition header apart from when it's in an email message, then the header controls behaviour as expected.

It's best to be specific about the file you're sending. Don't rely on the interpretation of the browsers in the face of missing or default headers.

Content-Length is good to set for downloads, since it will allow the browser to show a progress meter. It has to be accurate otherwise the browser will stall in downloading.

simon dot lee at terago dot ca
28-May-2002 07:46

There has been a nasty bug in IE 5 and even 6.0 (applied SP2 still couldn't fix it) which makes IE hangs when user cancels file download for several times (MS Knowledge base Q266305)

Here is my workaround:

header("Content-type: application/octet-stream");
header("Content-Disposition: filename=$myfile" . "%20");

In this case, we avoid the bug by not using "attachment" in Content-Disposition. Now, IE thinks that the file has an unknown extension which forces it to pop up the download box. When you save file, %20 doesn't get saved. However, IE does add [number] in the file name which is acceptable in my case.

E.g. filename.xls will become filename[1].xls

This is even a workaround for Q281119:  Internet Explorer Saves HTML Content Instead of the Active Document. (Especially we can't always assume users update their machine with lastest service packs)

Simon Lee
Webmaster@terago.ca
TeraGo Networks Inc.

simon dot lee at terago dot ca
28-May-2002 11:50

Additional notes to my workaround on Q266305:

I have tested my findings with PDF, XLS, DOC and ZIP. However JPG and GIF didn't make IE pop up the download box.  By changing content type from "octet-stream" to "force-download" (or some undefined type), it will work. Of course, we need to take care of other browsers too. Here is the improved code, hope it helps:

header("Content-type: application/force-download");

if (strstr($_SERVER["HTTP_USER_AGENT"], "MSIE"))
header("Content-Disposition: filename=$myfile" . "%20"); // For IE
else
 header("Content-Disposition: attachment; filename=$myfile"); // For Other browsers

Note: If you are using session together with this download, you will need to add the following line BEFORE the code above to make IE work:

session_cache_limiter("");

chrisshaffer at bellsouth dot net
31-May-2002 06:21

Not really sure why they made thier relative path/redirect example the way they did...  It works, but under a Win32 machine, it puts some extra slashes in the redirected path.

Hears what they had:
header("Location: http://"/.
$_SERVER['HTTP_HOST'].dirname($_SERVER['PHP_SELF']).
"/".$relative_url);

Here's what I got to work:

header("Location: http://".$_server['http_host'].$php_self/);

The $PHP_SELF variable gives its entire relative path.

13-Jun-2002 01:32
Note that some HTTP headers can't be rewritten under Apache. The Server: header in particular can't be overridden.

I've been trying to find documentation on this everywhere, but the only place I was able to find it was on the errata page for Apache Unleashed:
http://apacheunleashed.com/errata.html

If anyone can find where this is documented, please post it here.

ryanN0SPAM at dewarproperties dot com
19-Jun-2002 08:33

Someone asked above how to change the Server: HTTP response header in Apache.  Although you cannot change it dynamically in PHP, you do have some options in the httpd.conf file

The option in httpd.conf that controls the Server: header is ServerTokens and it can be one of four values:

VALUE                    MAKES Server: HEADER
-------------------------------------------------
ProductOnly             Server: Apache
Minimal                  Server: Apache/1.3.0
OS                       Server: Apache/1.3.0 (UNIX)
Full                     Server: Apache/1.3.0 (UNIX) PHP/3.0 mod_perl

For example, in httpd.conf, add the line:

ServerTokens Full

to change the Server header to:

Server: Apache/1.3.0 (UNIX) PHP/3.0 mod_perl

If you want to change it to anything else, you'll have to edit the source code.  I think the file to look at is http_core.c

php-related at vermonster dot com
19-Jul-2002 02:31

This may be obvious to some, but to send HTTP status code headers to IIS (i.e. unauthorized 401) use the "Status:" header and not "HTTP/":

header('Status: 401.5 Unauthorized');

dadarden_nospamola at iti2 dot nospamola dot net
19-Jul-2002 10:48

There is a nice example of header() usage at http://www.zend.com/zend/trick/tricks-august-2001.php
IE 5.5 must be patched at SP2 for it to work properly.

dadarden_nospamola at iti2 dot nospamola dot net
20-Jul-2002 12:38

If you use session_start() at the top of a php script that also has header() calls later in the script for a file download then you must add some form of cache control for IE to work properly.  I use header('Cache-Control: public'); immediately after the code at the top of the script with the session_start() call that verifies that I have a properly logged in user.  That allows the header() and fpassthru() calls to download a file later in the script using IE 5.5 SP2.
bilse at NOJUNK dot qbfox dot com
29-Jul-2002 02:34

Most people have been stumped by that, I think, but it's quite simple: The message you expect to see in your browser (either the canned "The requested URL blaah was not found on this server" or a custom error page) is served up by Apache when it can't find what was requested.  The message is not generated because a 404 (or other) header somehow went through the pipes.  The message is simply a human-readable version of the HTTP 404 header, supplied by Apache as a kind of courtesy in addition to the header, when it couldn't find what it was looking for.

Now, when you generate your own headers from within PHP, Apache isn't going to do anything.  It doesn't inspect the headers you generate, and if you generate a 404 (or other) error header, it isn't going to tag any kind of human-readable message onto what you produce.  As far as Apache is concerned, there is no 404 (or other) error condition -- after all, it found what was requested in the URL, and served it up.

Hence, for the user to see a 404 message, you need to produce some HTML yourself, in addition to sending the status header.  If you don't use a custom ErrorDocument, it's quite easy to mimic Apache's standard response. Otherwise, simply include() your errordocument after you've produced the headers.

lists at darkcore dot net
08-Aug-2002 09:24

Tips for sending files that are not in the web-path:

This may not be obvious to some but you should *NOT* use "include" to send a file because it tries to do some parseing of the file and will error on characters not recognized by PHP. 'include' will work on text files (like word documets etc.) but not files with binary content (like mp3, pdf, exe, etc).

For example, if you do the following:

$filename = "/var/private/acrobat.pdf";
header("Cache-control: private"); // fix for IE
header("Content-Type: application/octet-stream");
header("Content-Length: ".filesize($filename));
header("Content-Disposition: attachment; filename=acrobat.pdf");
include($filename); // ** WRONG **

You will likely get a plain text file that actually contans a bunch of PHP errors instead of the PDF you wanted.

The correct method (as someone already posted) is to:

$fp = fopen($filename, 'r');
fpassthru($fp); // ** CORRECT **
fclose($fp);

As far as I know, this works 100% of the time.

jpirkey at email dot com
13-Aug-2002 06:10

In attempting to download some rather large c++ source code files (1MB+) with Apache 2.0.39 and PHP 4.2.2 via the following header commands:
   header("Cache-control: private");
    header("Content-Type: text");
    header("Content-Length: 1290424");
    header("Content-Disposition:attachment; filename=module1.cc.001");

I ran into the issue of the files getting corrupted (i.e. files were scrambled and contained wierd control characters).  The files displayed fine if I did not try to download them but let the default content type take effect (text/html).

I fixed this by changing the "output_buffering" option in the php.ini to a value larger than my largested source file size 4000000 [~4MB].  The default size in the PHP 4.2.2 php.ini file is 4096 [4KB].

If someone else has any other suggestions concerning this matter, I check this documentation quite often or feel free to email me directly.

sean(at)localhardcore(dot)net
23-Aug-2002 09:55

Even better, use these headers, for mp3 downloading.  The above post works, but the download dialog wont give you an ETA of the download or file size.

header("Content-Type: application/mp3");
header("Content-Disposition: filename=$file");
header("Content-length: $size");
header("Cache-control: private");

j dot u-no-rsem-spam- at quicknet dot nl
28-Aug-2002 01:00

Here's one for all the people that really want to force any type of download, if it's text, or a word document, or anything else, this works.

[code]
$attachment = (strstr($HTTP_USER_AGENT, "MSIE")) ? "" : " attachment"; // IE 5.5 fix.
$string = "
<html>
<head>
<title>Hellooo world!</title>
</head>
<body>
<div style='position: absolute; left:200px; top:200px; width: 200px; height:200px; border: 1 solid #ABABAB; font-family:verdana; font-size:10px; font-weight:bold; background-color:#EFEFEF; color: #ABABAB;'>
Hi m8, i'm a little file that's just been downloaded to your pc :) </div>
</body>
</html>";
  header("Cache-control: private"); // another fix for IE
  header("Content-Type: application/octet-stream");
  header("Content-Length: ".strlen($string));
  header("Content-Disposition: attachment; filename=iamalittlehtmlfile."."html");
echo $string;
[/code]

you can replace the string by any file you just fpassthru. just make sure that the '.' for the filename is set before the extention that's concatted to it. I know it's weird, but if u don't do it like this, and try to send a word document, it will just open in the browser.

fschaper at zerodegree dot de
28-Aug-2002 11:14

If you want to send files via SSL and want to predefine the filename using something like this:

header("Content-type: application/octet-stream");  
header("Content-Disposition: attachment; filename=test.bin");

you _cannot_ use
header("Pragma: no-cache");  
or the filename will not be submitted like intended but the scriptname will be used ..

chen at hivemail dot com
30-Aug-2002 11:25

Here is a comprehensive list of MIME types you can send using the Content-Type header in a PHP array format and easy usage. Taken directly from Apache2's mime.type file, contains types for 138 file extensions:

http://www.hivemail.com/mime.types.phps

If $filename contains the name of the file you want to serve, use this code to send the right type:
header('Content-Type: ' . $mimetypes[substr(strrchr($filename, '.'), 1)]);

12-Sep-2002 11:47
One nice way of generating a report and/or allowing your users to export the contents of a table, is to return HTML table tags as content-type Excel.  This assumes the user has Excel 97 or later.

<?php
header('Content-type: application/vnd.ms-excel');
echo "<table><tr><td>hello</td><td>world</td></tr></table>";
?>

13-Sep-2002 10:10
The docs above show that the filename argument to Content-Disposition can be used without placing double quotes around the filename. The HTTP 1.1 spec specifically states that filename= must be followed by a quoted-string and is demonstrated by the example in the HTTP spec. The PHP docs should be changed to reflect this.
HTTP Spec example is at:
http://www.w3.org/Protocols/rfc2616/rfc2616-sec19.html#sec19.5.1
This may be the cause of peoples problems using this feature.

php at silisoftware dot com
15-Sep-2002 09:03

If you have a bunch of MP3s (or other file types that would be undesirably opened by an application) that you want to force to download rather than opening when clicked, you can simply create a .htaccess file containing:

AddType application/mp3 mp3

Put the .htaccess file in the directory you're serving the MP3s from (or a parent directory). "mp3" is the extension of the filetype whose behaviour you wish to modify, and "application/mp3" could be substituted with "application/pdf" or "application/octet-stream" or whatever.

mpriatel at rogers dot com
23-Sep-2002 09:41

Another general fix for many of the problems that occure when trying to prompt a filename to download is to append a '/' to your download URL. This fixes a NS7 problem where it wants to add a .PHP extension to all filenames.

For example, change:

http://www.url.com/download.php?file=23
to
http://www.url.com/download.php/?file=23

mikep at oeone dot com
01-Oct-2002 07:23

Netscape may give an error "Document contains no data" if the URL that you are forwarding the user to does not contain an ending slash.
To fix that problem, just append a slash to the URL before you forward them to it.

claude_minette at hotmail dot com
02-Oct-2002 02:03

Wanna know more about HTTP codes (404 and so on...)
http://www.indexa.fr/CodesHTTP.html
French site, but explanations in french and in english...

CYA Min's

cwcs at mac dot com
09-Oct-2002 01:11

The code above for outputting a file is not quite right:

> $fp = fopen($filename, 'r');
> fpassthru($fp); // ** CORRECT **
> fclose($fp);

If you read the documentation for fpassthru it says that after the file has been sent, the file is closed leaving the file pointer useless.  If you then try to close it using fclose, you get a warning displayed in the browser (and possibly tacked on to the end of your file!).  So the correct algorithm is simply:

$fp = fopen($filename, 'r');
fpassthru($fp);

andrew at frabjous dot org
09-Oct-2002 06:48

It looks like Status: only works when PHP is running as a CGI, and HTTP/ only works if it's running as a module.  Is this behaviour intentional?

In order to write portable code, it doesn't seem to hurt anything to include both versions.

pgcd at asphalto dot org
09-Oct-2002 01:33

I've been experiencing some problems with undesireable caching with phpMyAdmin and phpBB on my test system (PHP 4.2.3 and Apache 2.0.36 on a WinXP box) - IE6 absolutely *wanted* to cache everything, and there was no way to refresh the cache, except deleting all the files.
I accidentally stumbled on the solution today, though: it looks like something within the system (I suppose IE6, but I can't be sure) won't allow sending two "cache-control" headers, probably dismissing the first as soon as it receives the second. The problem was solved easily by putting everything on a single line and sending just one cache-control header.
Hope this helps (sure it would have helped *me* =) ).

info at keltoi-web dot com
09-Oct-2002 03:31

This full example works for IE6. It downloads the file "file".
<?
header ("Content-type: octet/stream");
header ("Content-disposition: attachment; filename=".basename($file).";");
header("Content-Length: ".filesize($file));
readfile($file);
exit;
?>
Hope it helps someone :).

webmaster AT f1-timer DOT de
13-Oct-2002 02:40

Regarding the Filename problems with NS7 / Mozilla when passing the filename via header("Content-Disposition:"). I have finally manged it to fix that problem with the *.php extension by sending a content-type of:

application/x-zip-compressed

and Mozilla does now recognize the .zip extension for my files.

Here is a short example:

if(stristr($HTTP_SERVER_VARS["HTTP_USER_AGENT"],"Gecko")) header("Content-type: application/x-zip-compressed\n");
else header("Content-type: application/zip");
header("Content-transfer-encoding: binary\n");
$head="Content-Disposition: attachment; filename=".$myname."\n";
 header($head);
header("Content-Length: ".$mylength);

Hope that helps someone, it took quite some time to figure out this problem...

15-Oct-2002 02:14
header("Status: 404 Not Found");


The second special case is the "Location:" header. Not only does it send this header back to the browser, but it also returns a REDIRECT (302) status code to the browser unless some 3xx status code has already been set.

header("Location: http://www.example.com/"); /* Redirect browser */
exit;                 /* Make sure that code below does
                        not get executed when we redirect. */


Note: HTTP/1.1 requires an absolute URI as argument to Location: including the scheme, hostname and absolute path, but some clients accept relative URIs. You can usually use $_SERVER['HTTP_HOST'], $_SERVER['PHP_SELF'] and dirname() to make an absolute URI from a relative one yourself:

header("Location: http://".$_server['http_host']/
                    .dirname($_SERVER['PHP_SELF'])
                   ."/".$relative_url);


PHP scripts often generate dynamic content that must not be cached by the client browser or any proxy caches between the server and the client browser. Many proxies and clients can be forced to disable caching with

header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");    // Date in the past
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
                                                   // always modified
header("Cache-Control: no-store, no-cache, must-revalidate");  // HTTP/1.1
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");                         // HTTP/1.0


Note: You may find that your pages aren't cached even if you don't output all of the headers above. There are a number of options that users may be able to set for their browser that change its default caching behavior. By sending the headers above, you should override any settings that may otherwise cause the output of your script to be cached.

Additionally, session_cache_limiter() and the session.cache_limiter configuration setting can be used to automatically generate the correct caching-related headers when sessions are being used.

Remember that header() must be called before any actual output is sent, either by normal HTML tags, blank lines in a file, or from PHP. It is a very common error to read code with include(), or require(), functions, or another file access function, and have spaces or empty lines that are output before header() is called. The same problem exists when using a single PHP/HTML file.

<?php header ("Content-type: audio/x-pn-realaudio"); ?>
// Broken, note the blank line above


Note: In PHP 4, you can use output buffering

jsheets at shadotech dot com
15-Oct-2002 09:12

For those of you who want to change a 404 (or other error) to a 200 OK status  this is how to do it.

header("status: 200") or header("http/1.0 200 Ok") depending on your configuration (my Apache required the second one).

The reason I needed to change the 404 to a 200 was I was doing intra site redirection and catching the 404 error so that I could display the appropiate file, with IE with friendly http error messages on (the default) IE displays its own 404 page and not yours so I changed the status to 200 OK and all is well.

a_alper at yahoo dot com
16-Oct-2002 01:48

Well, I had a slightly different problem. I wanted a page to open in ms word. Problem was when I used the following:

header("Content-type: application/x-msword");
It would open in textpad, which I have associated to open documents of type .php

Not what I wanted. What I DID want was for the document to open onscreen in word.  The webserver was, of course, giving the filename of the code page on the net, in this case labels.php and the resultant output was being associated with textpad due to the .php extension, even though the encoding type was x-msword.''

The fix was quite simple

header("Content-type: application/x-msword");
header("Content-Disposition: filename="labels.doc");

This solved the problem nicely. Without attachment or other declarations, the page opens properly in word as was the original intent. No download box is generated and word is the application invoked.

cyberwombat at yifan dot net
12-Nov-2002 06:16

Note that if you are writing to session in a page with header calls such as a redirect, you may have to call sessin_write_close() before the header command to make sure your that session variables stay written. Oddly enough, this behavior is inconsistent. I needed this function only when using cgi mode but not apache module mode.

Joshua

patric dot fornasier at swisscom dot com
16-Nov-2002 05:50

I had the problem that my files downloaded correctly, when I clicked 'save' on the download prompt box. But when I clicked 'open' I got an 'file not found error' inside, word, acrobat reader or whatever application.

I added "header("Cache-control: private");"

and it worked! :)

mark dot pearson at capita dot co dot uk
18-Nov-2002 06:48

If you are attempting to force file downloads using the
following headers

Content-Type: application/........
Content-Disposition: attachment; filename=file.ext

and find that with Internet Explorer 6 the browser asks you
twice whether you want to Open or Save the file, once with
the name of the web page (e.g. test.php) and then again
with the name given in the Content-Disposition header
(i.e. file.ext) then try the IE6 security fix at:

http://www.microsoft.com/technet/treeview/default.asp?
url=/technet/security/bulletin/MS02-005.asp

For the record I experienced this problem running
IE6 on Windows 2000 Server with IIS5 and PHP4.1.2.
File downloads worked perfectly with IE5.5 but as soon
as I upgraded to IE6 I started getting the annoying double
file download dialog. The fix mentioned above fixed the
problem for me.

I mention this problem and its solution here in the hope
it may be of use to other PHP developers working with
file downloads.

info at glenmurphy dot com
19-Nov-2002 12:16

In regard to the file downloading situation, please note that Mac IE will always use your php filename as the 'save as' document name, making these scripts useless in many situations.

I have seen a workaround that involved writing the url as:
http://server/script.php/filename.doc?file=filename.doc

However, this failed to work on the four servers (Apache2/PHP4 on (Win32|FreeBSD)) I've tried it on, producing either a 404 or a blank page - you may have better luck with Apache 1.3

adon at theDV8network dot com
20-Nov-2002 10:14

here's a rework of a previous example ..

i'm switching from shoutcast to icecast2 , and from .mp3 to .ogg formats ..  but my radio station has about 3 months of .mp3 archives .

here's a script to create a .pls file that will stream either .mp3 OR .ogg , AND will auto-download and (hopefully ) load winamp .

( check us out http://thedv8network.com/ _ free Radio and fertile resistances, fresh from the Canadian underground ) .

a:/,

____

<?php
if( isset($HTTP_GET_VARS['showcode']) ) $showcode = $HTTP_GET_VARS['showcode'];
if( isset($HTTP_GET_VARS['episode']) ) $episode = $HTTP_GET_VARS['episode'];
if( isset($HTTP_GET_VARS['quality']) ) $quality = $HTTP_GET_VARS['quality'];
if( isset($HTTP_GET_VARS['format']) ) $format = $HTTP_GET_VARS['format'];

$mountpoint="http://theDV8network.com:8004/dv8_shows/
{$showcode}/{$showcode}-{$episode}_{$quality}.
{$format}";

$attachment = (strstr($HTTP_USER_AGENT, "MSIE")) ? "" : " attachment"; // IE 5.5 fix.
$string = "[playlist]
File1={$mountpoint}
NumberOfEntries=1";
header("Cache-control: private"); // another fix for IE
header("Content-Type: application/pls");
header("Content-Length: ".strlen($string));
header("Content-Disposition: attachment; filename=theDV8network."."pls");
echo $string;
?>

___

23-Nov-2002 09:16
I actually want content to be cached for the next 30 days after it has been downloaded.  I am not an expert on the HTTP protocol - but I thik this will work:

$ts = date('D, d M Y H:i:s T', gmmktime(0,0,0,gmdate("m"),(gmdate("d")+30),gmdate("Y")));

header("Expires: $ts");    // Date 30 days time;
header("Cache-Control: max-age=2592000, min-fresh=2592000, max-stale=0");  // HTTP/1.1

chad dot miller at spam persidea dot com
04-Dec-2002 10:37

You might need to call session_write_close() prior to a header() call to redirect IF you're using CGI rather than a module.  We ran into this problem when we migrated an application from our server (Running the PHP Apache module) to another server (Running the CGI version).

For safety's sake, I recommend always calling session_write_close() before doing a redirect with header().  It'll make migrations much more painless!

jskuseNOSPAM at abstractdesignNOSPAM dot com
06-Dec-2002 11:52

There was a quick mention above about downloading files through SSL using IE. I was trying to write a script to send a file using https to a user. When the 'save as' window would pop up the name of the file I was tryin to send was being replaced with the scripts name and I would recieve an error.

After a bit of searching I found a reference in the session_cache_limiter() notes. I added session_cache_limiter('private') to my script before session_start().

Works without a problem now.

revol at post dot sk
19-Dec-2002 03:38

If You want to download a file from the server, the best way which I have found is the following:

list($adrs,$name)=explode("/",$file);
header('Content-Type: ' . $mimetypes[substr(strrchr($file, '.'), 1)]);
header ("Content-Disposition: attachment; filename=$name");
header ("Cache-control: private");
$fp = fopen('../files/'.$file, 'r');
fpassthru($fp);

Do not use "fclose($fp);", it will return You a string which will be writed to the end of the file and that can made some troubles if You are downloading for example a word doc.
Anyway, the function "fpassthru($fp);" will close the connection, so there is no reason to use fclose();

sgustafso at hotmail dot com
24-Dec-2002 07:16

It took me awhile to figure this one out, but using output buffering (at least for ob_gzhandler) with MSIE 6 can keep the filename parameter of Content-Disposition from working properly (Not sure about other browsers...). In my case, I had a file download.php with this:

header("Content-Type: application/x-ms-download");
header("Content-Disposition: attachment; filename=theme.xmb");
ob_start("ob_gzhandler")

Normally, that would offer a default filename of theme.xmb. But in this case, with the ob_start on, the browser attempts to name the file by the url; it wanted to name the file download, with the file type being a document.

zan at stargeek dot com
31-Dec-2002 07:39

an interesting example of how to use Header to create download-able excel files is here http://www.stargeek.com/scripts.php?script=2
jagarian at freechal dot com
03-Jan-2003 11:22

add header    php to excel~~~~  download file~
//####################################
$user_agent=${"HTTP_USER_AGENT"};

if ((strpos($user_agent,"MSIE") ? strpos($user_agent,"MSIE")+1 : 0)>0)
{

 if ((strpos($user_agent,"MSIE 5.0") ? strpos($user_agent,"MSIE 5.0")+1 : 0)>0)
 {

  $content_disp="attachment;filename=";
  $contenttype="application/vnd.ms-excel";
 }
   else
{

   $content_disp="attachment;filename=";
  $contenttype="application/vnd.ms-excel";
 }

}
else
{

 $content_disp="attachment;filename=";
$contenttype="application/vnd.ms-excel";
}

header("Content-Disposition".": "."attachment;filename=custmer.xls");
header("Content-type: ".$contenttype);
header("Cache-control: public");;
//####################################

tagg_maiwald at yahoo dot com
07-Jan-2003 03:26

For portable php [from dev to prod systems, for instance], you can conditionally code:
<?php
$sz_htstatus=('cgi'==php_sapi_name())?'Status:':'HTTP/1.0';
$sz_htstatus.=' 404 Not Found';
header ($sz_htstatus);
?>

hagman
14-Jan-2003 11:48

Found this source on caching.

http://www.web-caching.com/

venky_madurai at home dot com
17-Jan-2003 04:58

IE 55. sp2 and IE6 as on the date of adding this note have problems with content type gzip and caching http headers. The pages are never cached. I think this combination of http headers can also crash the browser.

see http://support.microsoft.com/default.aspx?scid=kb;en-us;321722

Michail A.Baikov
18-Jan-2003 06:20

For correct work with IE (without Page not found Error):

header("HTTP/1.0 301 Moved Permanently");
header("Location: ".$url);

change to:

header("HTTP/1.1 301 Moved Permanently");
header("Location: ".$url);
header("Connection: close");

dadarden at iti2 dot nospamola dot net
01-Feb-2003 03:56

If you have file download code using header() that works fine in IE when it runs under HTTP, but breaks when it runs under HTTPS, then go to comments of the php manual page on session_cache_limiter() for solutions.  For some reason IE uses the name of the php script as the filename of the file to download when the download code runs under https://.
mikezivin at yahoo dot com
11-Feb-2003 05:23

If you're looking to send back a header with a different status code, then the proper syntax is:

header("Location: HTTP/1.0 404");

The documentation is wrong. It says to do:

header("HTTP/1.0 404 Not Found");

THIS WILL NOT WORK! I tested it in both IE and Netscape and that header is never picked up by the browser.

flim at flim dot de
12-Feb-2003 04:34

I encountered problems with the header function after compiling php with --enable-mbstring ,--enable-mbstr-enc-trans and --enable-mbregex and setting mbstring.http_output = UTF-8 in php.ini. Afterwards, things like header('Location: index.php') did not work anymore (e.g. in phpmyadmin). The solution was to put mb_http_output("pass"); in the front of documents sending headers.
blue-siml at gmx dot net
09-Mar-2003 08:08

if you want prog a chat or other features that needs
a second script.. but the script shouldn replace the actually site.. than you can use

header("Content-type: ");

at this time the browser believe there is no content and the site that was in the browser before is still there..!

greeze,
siml
ps.: sorry for my bad english ;) i'm only a HTL school boy with 15 years..

Martin Rampersad (lastname at sympatico dot ca)
09-Mar-2003 10:24

(PHP 4.2.1, PHP 4.3.1)
If you use a header('Location:...') call in your code, PHP will _always_ force your document's HTTP status code to be 302 if you don't already specify a 3XX code (unless you use http_response_code, below).  However, the HTTP Specification states that an "HTTP/1.1 201 Created" is a valid status code to pair with a "Location:..." header.

http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.30

So, I upgraded to 4.3.1 but the problem remained, luckily the http_response_code option was added to the function call.  I used header('Location:...', TRUE, 201).  Now my server responses were getting generated properly.

Then, horror. Neither browser I used (IE 6 SP1, Mozilla/Phoenix) redirected to my location when I returned a "201 Created" code.

I'm not sure if 201 was ever intended for use with automatic redirection (probably not), but since my redirects don't work with it. I'm resigned to using "HTTP/1.1 302 Found" redirects.

I guess the only problem I see is PHP not letting a header('HTTP/1.1 201 Created') exist with a header('Location:...') without some forcing.

no spam toby dot adm at miracles dot de
16-Mar-2003 11:06

If you are experiencing problems with sending .zip-files to IE, where they download fine but appear to be broken when opened directly, you should try adding

header("Cache-control: private");

That should take care of the problem.

Seems to be a similar problem as Patric mentioned in his above post. Thank you, Patric :)

nospam_andy at netcafe-konstanz dot de
18-Mar-2003 05:41

[Editor's Note] You can change the response code to 200 OK by header("status: 200") or header("http/1.0 200 Ok") depending on your configuration which will prevent IE from displaying a 404 page.[/Note]

If you ever wanted to define custom ErrorDocuments in Apache, you might have experienced problems with IE browsers. This f****** chunk of software uses it's own standard error documents which often aren't very useful. Nevertheless to convince this piece of s*** of doing what it should have to do, you have to feed it with some extra header-information. Just send a "WWW-Authenticate:" response-header field with arbitrary content, e.g. take the first example from this site header('WWW-Authenticate: Negotiate').

Don't know why, but it works...

greetz from germany,

Andy

P.S.: Check this out: http://www.laut.de/

Ze`ev @ CENTURi
18-Mar-2003 11:21

As a matter of fact IE will display custom error pages if they are larger than roughly 11kb. Anything smaller gets replaced by the standard defaults, and then you need all kinds of header trickery :)
kramer at irc dot pl
21-Mar-2003 09:21

Yo!
Looks like Netscape 7 doesn't like download files with all
above mentioned examples. I tested all of them and still
NN like to save any file with php extension. Instead this I
use header like this:
header("Location: http://192.168.0.2/dml.zip");
Work fine.

brgds to all contributors, kramer

bebop at gmx dot at
07-Apr-2003 01:07

in other forums it was often asked, when using frames how to forward to a page that shall be on _top.
now the problem is that you CAN'T do that with PHP.
some workaround:
forward to the frameset and provide information which file should be shown in what frame:

<FRAMESET ...>
   <FRAME SRC=<?= $nav ?> ...>
   <FRAME SRC=<?= $top ?> ...>
   <FRAME SRC=<?= $content ?> ...>
</FRAMESET>

or do

if ($forward)
{
  ?><body>
  ...
  </body><?
 }
else
{
  ?><frameset>
  ...
  </frameset><?
}

pmdMadrid at hotmail dot com
07-Apr-2003 06:59

I'm using the header to download a rpm file.  First I used the following code to upload the file:

$fp=fopen("$filename", 'r');
fpassthru($fp);
fclose($fp)

The file download fine, but the size of that was different from the original

Then I change that code by

readfile("$filename");

and all was fine.

joakim at locotech dot fi
08-Apr-2003 09:08

There's been a lot of talk about the .php -extension being added to the file while forcing downloads to a Mozilla Client.

I had the same problem and followed all the documented suggestions found here. Still, nothing seemed to work. I added the slash before the "?" in the url: send.php/?send=7 and so forth.

Then I discovered the last file worked, but not the three first ones in my list of downloadables. This is because there are scandinavian chars "ä" and "ö" (a and o -umlauts) in the first three filenames. So naturally I wrote a quick fix to replace those chars if "Gecko" is found in the user_agent variable and now it works:

$user_agent=${"HTTP_USER_AGENT"};
if (strpos($user_agent,"Gecko")) $filename=str_replace
("ö","o",str_replace("ä","a",strtolower($filename)));
$file="files/$send";
header("Content-type: octet/stream\n");
header("Content-transfer-encoding: binary\n");
$head="Content-Disposition: attachment; filename=".$filename."\n";
header($head);
header("Content-Length: ".filesize($file));
readfile($file);
exit;

I am also changing the filename to lowercase so that I don't have to check for large Ä:s and Ö:s. Yes, I am lazy. :)

Hope this helps.

-Joakim

markph at speakeasy dot net
11-Apr-2003 02:17

I was doing this:

require_once('useragent.php');
$agent = new UserAgent();
if ($agent->isPPC())
{
    session_write_close();
    header("Location: http://www.slashdot.org/palm/");
   exit;
}

It works, but ONLY if you ensure you leave NO spaces around the <?php ...   ?>  in the useragent.php file.

This basically means that php will write any empty lines back, you cannot redirect if you've written back to the client.

This is all cached before being sent back to the browser, I have not yet dug up how to empty the cache in php.

Mark

matmoss at club-internet dot fr
15-Apr-2003 05:37

Sending a file - mp3 for instance - using header statement, it seems windows/msie (6.0..) doesn't care about mime type sent using header("Content-Type: $mime_type") statement but rather guess the type of the file from the file extension in the header("Content-Disposition: attachment; filename=$filename") statement.
Therefore one must absolutely specify an extension in order to have the file correctly handled by the browser and the dedicated application for that type of file, else the shell will ask for choosing an application to handle this file type. In an other hand netscape navigator seems to take into account the mime type provided in the header information whatever extension the file has. if no header("Content-Type: $mime_type") is specified netscape will not handle the file, displaying it as is in the browser while msie will handle it correctly as long as the file extension is provided.
In conclusion you've got to set carefully the mime-type and the filename extension in order to have your fie be handled the way you want in both browser.
cheers

james at SORRYNOSPAM dot com
17-Apr-2003 12:00

If you try to redirect your page with the Header instruction, as:

Header('Location: http://www.spaccawarrant.it/index.html');

and this is done inside one of your page frames, the page you asked for will be opened inside that frame.

How to get on the _top page? I found no solution with php, but with a very simple javascript:

Just call a very short page with this small javascript inside

<html>
<body>
<SCRIPT LANGUAGE="JavaScript">
top.location.replace ('http://www.spaccawarrant.it/index.html')
</SCRIPT>
</body>
</html>

and you'll be able to clear all frames!

Jonas at middle sweden
23-Apr-2003 02:34

In response to: [rlourenco at uol dot com dot br 21-Feb-2002 06:26]

Please, note that Macintosh computers have a very strict file type assignment system. if a word document is downloaded to a macintosh with Netscape 7 without the trailing ".doc" and the header sent is:

header("Content-type: application/octet-stream");

...the user won't be able to open the file with Microsoft Word. This is due to the file type registered by the file system. The type of a file is not very easily changed on a MacOS system, so to avoid that particular problem, send this header instead:

header("Content-type: application/msword");

emil dot assarsson at delete dot bolina dot hsb dot se
26-Apr-2003 11:54

Aboute names on PHP-generated files:
If you refer to the script with an extra path (pathinfo) like this
<a href="/genfile.php/filename.pdf">download file</a>
then the downloaded filename will be correct even on buggy IEs.
This is a fine solution if the refering page know the filename.
This works with Apache. I'm not sure if there is any problems with other servers.

And another thing... Allways use correct Content-Type AND file suffix. This eliminates a lot of bugs. If a filetype don't have a registered mime-type, please tell this to the developers (this should be considered as a bug today).

meet_mr_ego at hotmail dot com
07-May-2003 05:15

Try This:

function pass_out($file) {
header("Content-Type: " . filetype($file));

$open = fopen($file, "r");
fpassthru($open);
fclose($open);
}

kbrown at dcwasa dot com
09-May-2003 10:10

Hope this saves time.  If you're trying to use the
header tag to redirect a page and meta refresh isn't working, try

header("Refresh: 3; URL=/index.phpnn");

where 3 is the number of seconds you want to wait before redirect,
and /index.phpnn is the page you'd like to redirect to.

hbrindis at example dot com
10-May-2003 02:26

mikep's tip above fixes well the 'document contain no data' bug for Netscape, but only if you want to redirect to a plain URL. If you want to pass variables to a script within header( ), you can add an '&' before the '/':

$Url = "Location: http://myserver.com/admin/index.php?var=".$var."&/";
header($Url);

..this way index.php can parse $var properly.

P.S. If you concatenate too much variables within the header(), it may not get the last ones, so it's better to do it outside the function.

jmertic at ncscredit dot com
13-May-2003 10:03

I've been trying to validate PDF Files on Win2k/IIS 5/PHP 4.3.1. Everything worked fine on Netscape/Mozilla, but IE said it could not find the file. I now use the following method, and all seems to work well now:

$file = $_GET['uri'];    // filename is passed via GET variable
$len = filesize($file);
header("Cache-Control: public");
header("Content-Type: application/pdf");
header("Content-Disposition: inline; filename=" . $_GET['uri'] . "");
header("Accept-Ranges: bytes");
header("Content-Length: $len");
readfile($file);

The "Accept-Ranges: bytes" seemed to be the key to solving this problem. "Cache-Control: public" was needed in order to use my cookie-based auth scheme.

kevin at sylandro dot com
22-May-2003 07:03

If you're using PHP to generate a style-sheet file, you should use:

header("Content-Type: text/css");

rajka at 6deex dot net
22-May-2003 01:56

I was trying to download a CSV file using IE6 SP1, but it was displayed in the window by excel: I was using "Application/octet-stream" as Content-type. So after a little search, I found a type/subtype pair that works, the code looks like this:

header("Content-type: text/tab-separated-values");
header("Content-Length: ".filesize($fpath));
header("Content-Disposition: filename=".basename($fpath));
readfile($fpath);
exit;

I stripped out every error check, just to shorten the code. None of the users notes worked for me, I hope this will be useful for somebody.

add a note add a note

<HTTP headers_sent>
  Last updated: Sat, 19 Apr 2003
show source | credits | mirror sites 
Copyright © 2001-2003 The PHP Group
All rights reserved.
This mirror generously provided by: Schlund + Partner
Last updated: Fri May 23 00:09:31 2003 CEST